/**
 * @author Jim
 * @create 2025/1/13
 */
package com.example.ping

import com.example.ping.controller.PingController
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import spock.lang.Specification
import spock.lang.Subject

import java.time.LocalDateTime

@SpringBootTest
@EnableScheduling
class PingTaskTest extends Specification {

    @Autowired
    StringRedisTemplate redisTemplate

    @Subject
    PingController pingTask

    def setup() {
        pingTask = new PingController()
        pingTask.redisTemplate = redisTemplate
        pingTask.webClient = Mock(WebClient)
    }

    def "test sendPing when under rate limit"() {
        given: "Redis is initialized with no request count"
        redisTemplate.delete("ping_rate_limit")

        and: "Mock WebClient responds successfully"
        WebClient.RequestHeadersUriSpec requestSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()
        pingTask.webClient.get() >> requestSpec
        requestSpec.uri("/pong") >> requestSpec
        requestSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String) >> Mono.just("World")

        when: "sendPing is called"
        pingTask.sendPing()

        then: "Request count in Redis increments"
        redisTemplate.opsForValue().get("ping_rate_limit") as Integer == 1

        and: "WebClient is called"
        1 * responseSpec.bodyToMono(String)

        and: "Log shows successful response"
        noExceptionThrown()
    }

    def "test sendPing when rate limited"() {
        given: "Redis request count exceeds MAX_RPS"
        redisTemplate.opsForValue().set("ping_rate_limit", "3")

        when: "sendPing is called"
        pingTask.sendPing()

        then: "Request is not sent due to rate limiting"
        0 * pingTask.webClient.get()

        and: "Log shows rate limit message"
        noExceptionThrown()
    }

    def "test sendPing with Redis unavailable"() {
        given: "Redis is unavailable"
        pingTask.redisTemplate = Mock(StringRedisTemplate)
        pingTask.redisTemplate.opsForValue() >> { throw new RuntimeException("Redis down") }

        when: "sendPing is called"
        pingTask.sendPing()

        then: "Exception is handled gracefully"
        noExceptionThrown()
    }

    def "test sendPing when WebClient throws error"() {
        given: "Mock WebClient throws error"
        WebClient.RequestHeadersUriSpec requestSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()
        pingTask.webClient.get() >> requestSpec
        requestSpec.uri("/pong") >> requestSpec
        requestSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String) >> { throw new RuntimeException("Connection error") }

        when: "sendPing is called"
        pingTask.sendPing()

        then: "Error is logged"
        noExceptionThrown()
    }
}
